package workPoll

import (
	"fmt"
	"math/rand"
	"sync"
	"time"
)

/** 工作池

缓冲信道的重要应用之一就是实现工作池[8]。

一般而言，工作池就是一组等待任务分配的线程。一旦完成了所分配的任务，这些线程可继续等待任务的分配。
*/

func Test() {
	startTime := time.Now()
	noOfJobs := 100
	go allocate(noOfJobs)
	done := make(chan bool)
	go res(done)
	noOfWorkers := 10
	createWorkerPool(noOfWorkers)
	<-done
	endTime := time.Now()
	diff := endTime.Sub(startTime)
	fmt.Println("total time taken ", diff.Seconds(), "seconds")
}

// 我们会使用缓冲信道来实现工作池。我们工作池的任务是计算所输入数字的每一位的和。例如，如果输入 234，
//结果会是 9（即 2 + 3 + 4）。向工作池输入的是一列伪随机数。

//我们工作池的核心功能如下：
//创建一个 Go 协程池，监听一个等待作业分配的输入型缓冲信道。将作业添加到该输入型缓冲信道中。
//作业完成后，再将结果写入一个输出型缓冲信道。从输出型缓冲信道读取并打印结果。

// 1.创建结构体
type job struct {
	id     int
	random int
}
type result struct {
	job         job
	sumofdigits int
}

// 定义有缓冲的信道
var jobs = make(chan job, 10)
var results = make(chan result, 10)

// 计算整数的每一位之和
func digits(number int) int {
	sum := 0
	no := number
	for no != 0 {
		digit := no % 10
		sum += digit
		no /= 10
	}
	time.Sleep(2 * time.Second) //模拟计算过程中花费的时间
	return sum
}

// 创建工作协程函数
func worker(wg *sync.WaitGroup) {
	defer wg.Done() //计数器减1
	for j := range jobs {
		output := result{j, j.random}
		results <- output //写入结果信道
	}
}

// 创建协程的工作池
func createWorkerPool(workers int) {
	var wg sync.WaitGroup
	for i := 0; i < workers; i++ {
		wg.Add(1)
		go worker(&wg)
	}
	wg.Wait() //等待计数器结束
	close(results)
}

// 创建分配工作函数
func allocate(noOfJobs int) {
	for i := 0; i < noOfJobs; i++ {
		random := rand.Intn(999)
		j2 := job{i, random}
		jobs <- j2
	}
	close(jobs)
}

// 创建读取results 结果信道函数
func res(done chan bool) {
	for r := range results {
		fmt.Println("读取结果信道内容：", r)
	}
	done <- true
}
